#include "ScreenThumbnailHelper.h"

///Copyright (c) 2009-2010 T. Klepzig 

map<HWND, ScreenThumbnailHelper*> ScreenThumbnailHelper::instances;
int ScreenThumbnailHelper::virtualscreenXOffset=0;
int ScreenThumbnailHelper::virtualscreenYOffset=0;


ScreenThumbnailHelper::ScreenThumbnailHelper(HWND hWnd)
{
	if (instances.find(hWnd) != instances.end())
		throw -1;

	this->hWnd=hWnd;
	instances[hWnd]=this;
}

ScreenThumbnailHelper::~ScreenThumbnailHelper()
{
	instances.erase(hWnd);
}


//Image* ScreenThumbnailHelper::CaptureWindow(HWND hWnd)
//{
//    Image *result=0;
//
//    if (hWnd)
//    {
//		RECT r;
//		GetWindowRect(hWnd, &r);
//        Size size(RECTWIDTH(r), RECTHEIGHT(r));
//
//        if (!size.Empty())
//        {
//            Bitmap *bmp=new Bitmap(size.Width, size.Height);
//
//            Graphics *g = Graphics::FromImage(bmp);
//
//            HDC dc = g->GetHDC();
//            PrintWindow(hWnd, dc, 0);
//            g->ReleaseHDC(dc);
//
//			delete g;
//
//            result = bmp;
//        }
//    }
//
//    return result;
//}
//
//Image* ScreenThumbnailHelper::CaptureWindow(HWND hWnd, Size s)
//{
//	if (s.Empty())
//        return CaptureWindow(hWnd);
//    else
//        return CaptureWindow(hWnd)->GetThumbnailImage(s.Width, s.Height);
//}



//Image* ScreenThumbnailHelper::CreateScreenshot(set<HWND> windowsToInclude, POINT offset, RECT rect)
//{
//	RefreshScreenThumbnailAreaData(rect);
//
//	this->windowsToInclude=windowsToInclude;
//	EnumWindows(EnumWindowsProc,(LPARAM)hWnd);
//
//    //Reverse for right z-order
//	windows.reverse();
//
//	RECT r;
//	GetWindowRect(hWnd, &r);
//	Bitmap *bmp=new Bitmap(RECTWIDTH(r), RECTHEIGHT(r));
//	Graphics *graphics = Graphics::FromImage(bmp);
//
//	list<HWND>::iterator iter;
//	for(iter=windows.begin();iter!=windows.end();iter++)
//	{
//		RECT windowRect;
//		GetWindowRect(hWnd, &windowRect);
//
//		if (!IsIconic(*iter))
//		{
//			windowRect.left += offset.x;
//			windowRect.right += offset.x;
//
//			windowRect.top += offset.y;
//			windowRect.bottom += offset.y;
//
//
//			windowRect.left += virtualscreenXOffset;
//			windowRect.right += virtualscreenXOffset;
//
//			windowRect.top += virtualscreenYOffset;
//			windowRect.bottom += virtualscreenYOffset;
//
//
//			windowRect.left = (int)(windowRect.left * FactorX);
//			windowRect.right = (int)(windowRect.right * FactorX);
//		
//			windowRect.top = (int)(windowRect.top * FactorY);
//			windowRect.bottom = (int)(windowRect.bottom * FactorY);	
//
//
//			windowRect.left += rect.left;
//			windowRect.right += rect.left;
//
//			windowRect.top += rect.top;
//			windowRect.bottom += rect.top;
//
//
//			Size s(RECTWIDTH(windowRect), RECTHEIGHT(windowRect));
//			Image *tmp = CaptureWindow(*iter, s);
//			graphics->DrawImage(tmp, windowRect.left, windowRect.top, RECTWIDTH(windowRect), RECTHEIGHT(windowRect));
//			delete tmp;
//		}
//	}
//
//	delete graphics;
//	return bmp;
//}

void ScreenThumbnailHelper::RefreshScreenThumbnailAreaData(RECT rect)
{
    virtualscreenXOffset = 0;
    virtualscreenYOffset = 0;

	EnumDisplayMonitors(0,0,EnumDisplayMonitorsProc,0);

    virtualscreenXOffset = abs(virtualscreenXOffset);
    virtualscreenYOffset = abs(virtualscreenYOffset);

    FactorX = (double)((double)RECTWIDTH(rect) / (double)GetSystemMetrics(SM_CXVIRTUALSCREEN));
    FactorY = (double)((double)RECTHEIGHT(rect) / (double)GetSystemMetrics(SM_CYVIRTUALSCREEN));
}

void ScreenThumbnailHelper::CreateSimulatedScreenThumbnail(set<HWND> windowsToInclude, POINT screenOffset, RECT rect)
{
	RefreshScreenThumbnailAreaData(rect);

	windows.clear();

	this->windowsToInclude=windowsToInclude;
	EnumWindows(EnumWindowsProc,(LPARAM)hWnd);

    //Reverse for right z-order
	windows.reverse();

	list<HWND>::iterator iter;
	for(iter=windows.begin();iter!=windows.end();iter++)
	{
        RECT windowRect;

		if (IsIconic(*iter))
		{
			//minimized
			WINDOWPLACEMENT wp={0};
			GetWindowPlacement(*iter, &wp);

			if ((wp.flags & WPF_RESTORETOMAXIMIZED) == WPF_RESTORETOMAXIMIZED)
			{
				//minimized and maximized, when restored
				windowRect.left = wp.ptMaxPosition.x;
				windowRect.top = wp.ptMaxPosition.y;
				windowRect.right = windowRect.left + GetSystemMetrics(SM_CXMAXIMIZED);
				windowRect.bottom = windowRect.top + GetSystemMetrics(SM_CYMAXIMIZED);
			}
			else
			{
				//only minimized
				windowRect = wp.rcNormalPosition;
			}
		}
		else
		{
			//normal window state
			GetWindowRect(*iter, &windowRect);
		}


		windowRect.left += screenOffset.x;
		windowRect.right += screenOffset.x;

		windowRect.top += screenOffset.y;
		windowRect.bottom += screenOffset.y;
		

		windowRect.left += virtualscreenXOffset;
		windowRect.right += virtualscreenXOffset;		
	
		windowRect.top += virtualscreenYOffset;
		windowRect.bottom += virtualscreenYOffset;


		bool cut = false;
		RECT clientRect;
		GetClientRect(hWnd, &clientRect);

		RECT rcSource = {clientRect.left, clientRect.top, clientRect.left + RECTWIDTH(windowRect), clientRect.top + RECTHEIGHT(windowRect)};

		if (windowRect.left < GetSystemMetrics(SM_XVIRTUALSCREEN))
		{
			cut = true;
			rcSource.left = GetSystemMetrics(SM_XVIRTUALSCREEN) - windowRect.left;
			rcSource.right -= GetSystemMetrics(SM_XVIRTUALSCREEN) - windowRect.left;
			
			windowRect.left = GetSystemMetrics(SM_XVIRTUALSCREEN);
		}			
		if (windowRect.right > GetSystemMetrics(SM_CXVIRTUALSCREEN))
		{
			cut = true;
			rcSource.right -= windowRect.right - GetSystemMetrics(SM_CXVIRTUALSCREEN);
			
			windowRect.right = GetSystemMetrics(SM_CXVIRTUALSCREEN);
		}	

		if (windowRect.top < GetSystemMetrics(SM_YVIRTUALSCREEN))
		{
			cut = true;
			rcSource.top = GetSystemMetrics(SM_YVIRTUALSCREEN) - windowRect.top;
			rcSource.bottom += GetSystemMetrics(SM_YVIRTUALSCREEN) - windowRect.top;
			
			windowRect.top = GetSystemMetrics(SM_YVIRTUALSCREEN);
		}
		if (windowRect.bottom > GetSystemMetrics(SM_CYVIRTUALSCREEN))
		{
			cut = true;
			rcSource.bottom -= windowRect.bottom - GetSystemMetrics(SM_CYVIRTUALSCREEN);
			
			windowRect.bottom = GetSystemMetrics(SM_CYVIRTUALSCREEN);
		}


		windowRect.left = (int)(windowRect.left * FactorX);
		windowRect.right = (int)(windowRect.right * FactorX);
		
		windowRect.top = (int)(windowRect.top * FactorY);
		windowRect.bottom = (int)(windowRect.bottom * FactorY);	


		windowRect.left += rect.left;
		windowRect.right += rect.left;

		windowRect.top += rect.top;
		windowRect.bottom += rect.top;	
			

		HTHUMBNAIL hThumbnail=0;
		if (!DwmRegisterThumbnail(hWnd, *iter, &hThumbnail))
		{
			DWM_THUMBNAIL_PROPERTIES thumbnailProperties={0};
			thumbnailProperties.dwFlags = DWM_TNP_SOURCECLIENTAREAONLY | DWM_TNP_VISIBLE | DWM_TNP_OPACITY | DWM_TNP_RECTDESTINATION/* | DWM_TNP_RECTSOURCE*/;
			thumbnailProperties.fSourceClientAreaOnly = FALSE;
			thumbnailProperties.opacity = 255;
			thumbnailProperties.fVisible = true;

			if (cut)
			{
				thumbnailProperties.dwFlags |= DWM_TNP_RECTSOURCE;
				thumbnailProperties.rcSource = rcSource;
			}

			thumbnailProperties.rcDestination = windowRect;
			DwmUpdateThumbnailProperties(hThumbnail, &thumbnailProperties);
			hThumbnails.push_back(hThumbnail);
		}
	}
}

void ScreenThumbnailHelper::DestroySimulatedScreenThumbnail()
{
	list<HTHUMBNAIL>::iterator iter;
	for(iter=hThumbnails.begin();iter!=hThumbnails.end();iter++)
	{
		DwmUnregisterThumbnail(*iter);
	}
	hThumbnails.clear();
}


BOOL CALLBACK ScreenThumbnailHelper::EnumWindowsProc(HWND hWnd, LPARAM lParam)
{
	if (instances[(HWND)lParam]->windowsToInclude.find(hWnd) != instances[(HWND)lParam]->windowsToInclude.end()
		&& !IsIconic(hWnd)/*remove condition for include also minimized windows into the screenThumbnail*/)
			instances[(HWND)lParam]->windows.push_back(hWnd);

	return TRUE;
}

BOOL CALLBACK ScreenThumbnailHelper::EnumDisplayMonitorsProc(HMONITOR hMonitor, HDC hdc, LPRECT lpRect, LPARAM lParam)
{
	if (lpRect->left < 0 && lpRect->left < virtualscreenXOffset)
		virtualscreenXOffset = lpRect->left;

	if (lpRect->top < 0 && lpRect->top < virtualscreenYOffset)
		virtualscreenYOffset = lpRect->top;

	return TRUE;
}
